package org.eclipse.php.internal.console.ui.editor;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.php.internal.console.ui.view.HistoryElement;
import org.eclipse.php.internal.console.ui.view.HistoryListModel;
import org.eclipse.php.internal.console.ui.view.HistoryView;
import org.eclipse.php.internal.console.ui.view.PHPConsoleVariablesView;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;

public class ConsoleEditor extends EditorPart implements ISelectionProvider,
		ISelection {

	private static final String prompt=">> ";
	private static Pattern p=Pattern.compile("[_0-9a-zA-Z]*");
	private static Pattern l=Pattern.compile("[[_0-9a-zA-Z] ]*");
	private HistoryListModel historyListModel=new HistoryListModel();
	private StyledText inputArea;
	private int lastPosition;
//	private StringBuffer buffer=new StringBuffer();
//	private Stack execStack=new Stack();
	private long seq=1;
	private static Stack commandList=new Stack();
	private int commandPointer=0;
	private IProject project;
	private String sourcePath;
	private boolean isSaved=true;
	private Menu popupMenu;
	private MenuItem engineMenuItem;
	private Menu subMenu;
	private MenuItem[] subMenus;

	public ConsoleEditor() {
		super();
	}

	public void updateTitle() {
		IEditorInput input=getEditorInput();
		setPartName(input.getName());
		setTitleToolTip(input.getToolTipText());
	}

//	private void loadSession(String path) {
//		try {
//			FileInputStream fis=new FileInputStream(path);
//			ObjectInputStream ois=new ObjectInputStream(fis);
//			Hashtable hashtable=(Hashtable) ois.readObject();
//			List historyList=(List) ois.readObject();
//			this.historyListModel.clearHistoryListModel();
//			historyListModel.setHistoryList(historyList);
//			// seq = historyListModel.getHistoryList().size() + 1;
//			ois.close();
//			setFocus();
//		} catch (Exception e) {
//			// e.printStackTrace();
//		}
//	}

	private void saveSession(String path) {
		try {
			FileOutputStream fos=new FileOutputStream(path);
			ObjectOutputStream oos=new ObjectOutputStream(fos);
			oos.writeObject(getHistoryListModel().getHistoryList());
			oos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void doSave(IProgressMonitor monitor) {
		String filePath=((FileEditorInput) getEditorInput()).getPath()
				.toString();
		saveSession(filePath);
		isSaved=true;
		historyListModel.setCleared(false);
	}

	private IFile findResource(IProject project, String name)
			throws CoreException {
		project.refreshLocal(IResource.DEPTH_ZERO, null);
		IResource[] resources=project.members();
		for (int i=0; i < resources.length; i++) {
			IResource member=resources[i];
			if (member.getName().equals(name) && member.exists()
					&& member instanceof IFile) {
				return (IFile) resources[i];
			}
			if (resources[i] instanceof IFolder) {
				IFile temp=findResource((IFolder) resources[i], name);
				if (temp != null) {
					return temp;
				}
			}
		}
		return null;
	}

	private IFile findResource(IFolder folder, String name)
			throws CoreException {
		folder.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
		IResource[] resources=folder.members();
		for (int i=0; i < resources.length; i++) {
			IResource member=resources[i];
			if (member.getName().equals(name) && member.exists()
					&& member instanceof IFile) {
				return (IFile) resources[i];
			}
			if (resources[i] instanceof IFolder) {
				return findResource((IFolder) resources[i], name);
			}
		}
		return null;
	}

	public void doSaveAs() {
		Shell shell=getSite().getShell();
		FileDialog fileDialog=new FileDialog(shell, SWT.SAVE);
		fileDialog.setFilterExtensions(new String[] { "*.i" });
		fileDialog.setFilterPath(((FileEditorInput) getEditorInput()).getPath()
				.toString());
		fileDialog.open();
		String fileName=fileDialog.getFileName();

		if (fileName != null && !"".equals(fileName.trim())) {
			saveSession(fileDialog.getFilterPath() + File.separator + fileName);
			IFile findFile=null;
			try {
				findFile=findResource(project, fileName);
				IDE.openEditor(getEditorSite().getPage(), findFile);
			} catch (PartInitException e) {
				e.printStackTrace();
			} catch (CoreException e) {
				e.printStackTrace();
			}
		}
		historyListModel.setCleared(false);
	}

	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		setSite(site);
		setInput(input);
	}

	public boolean isDirty() {
		return false;
	}

	public boolean isSaveAsAllowed() {
		return true;
	}

	public void createPartControl(Composite parent) {
		this.inputArea=new StyledText(parent, SWT.H_SCROLL | SWT.V_SCROLL);
		initialize();
		setPartName(getEditorInput().getName());
		getSite().setSelectionProvider(this);
	}

	class MouseHandler extends MouseAdapter {

		public void mouseUp(MouseEvent e) {
			if (e.button == 3) {
				popupMenu.setVisible(true);
			}
		}
	}

	public void createPopupMenu() {
		IWorkbench workbench=PlatformUI.getWorkbench();
		Shell shell=workbench.getActiveWorkbenchWindow().getShell();
		popupMenu=new Menu(shell, SWT.POP_UP);
		engineMenuItem=new MenuItem(popupMenu, SWT.CASCADE);
		engineMenuItem.setText("Engine");
		subMenu=new Menu(engineMenuItem);
		engineMenuItem.setMenu(subMenu);
	}

	private void initialize() {
		IResource resource=((IFileEditorInput) getEditorInput()).getFile();
		this.project=resource.getProject();
		this.sourcePath=project.getFolder("Source").getLocation().toString();

		createPopupMenu();
		inputArea.addMouseListener(new MouseHandler());

//		String filePath=((FileEditorInput) getEditorInput()).getPath()
//				.toString();
//		loadSession(filePath);
		lastPosition+=prompt.length() + 1;
		inputArea.append(1 + prompt);
		inputArea.addKeyListener(new InputKeyListener());
		inputArea.addVerifyKeyListener(new InputVerifyKeyListener());
	}

	public class InputVerifyKeyListener implements VerifyKeyListener {

		public void verifyKey(VerifyEvent event) {
			char ch=event.character;
			if (ch == '\r') {
				int n=inputArea.getText().length();
				inputArea.setCaretOffset(n);
				event.doit=true;
			}
		}
	}

	public class InputKeyListener implements KeyListener {

		InputKeyListener() {
		}

		public void keyPressed(KeyEvent event) {
			int ch=event.keyCode;
			if (ch == SWT.HOME) {
				inputArea.setCaretOffset(lastPosition);
				return;
			}
			if (ch == SWT.END) {
				inputArea.setCaretOffset(inputArea.getText().length());
			}

			if (ch == SWT.BS || ch == 65536) {
				int newPosition=inputArea.getCaretOffset();
				if (newPosition <= lastPosition) {
					event.doit=false;
					// workaround since consume on backspace key does not work
					inputArea.setCaretOffset(lastPosition);
					inputArea.insert(" ");
					inputArea.setCaretOffset(lastPosition);
					return;
				}
			}

			if (ch == SWT.LEFT || ch == 16777219) {
				int newPosition=inputArea.getCaretOffset();
				if (newPosition <= lastPosition) {
					inputArea.setCaretOffset(lastPosition);
					event.doit=false;
					return;
				}
			}

			if (ch == SWT.ARROW_UP || ch == SWT.ARROW_DOWN) {
				int size=commandList.size();
				if (ch == SWT.ARROW_DOWN) {
					commandPointer++;
					if (commandPointer == size)
						commandPointer=0;
				} else if (ch == SWT.ARROW_UP) {
					commandPointer--;
					if (commandPointer == -1)
						commandPointer=size - 1;
				}
				String in=(String) commandList.get(commandPointer);
				in=in.trim();
				int textAreaLength=inputArea.getText().trim().length();
				String previousText=inputArea.getText().substring(0,
						lastPosition);
				inputArea.setText(previousText + in);
				textAreaLength=inputArea.getText().trim().length();
				inputArea.setCaretOffset(textAreaLength);
				event.doit=false;
				inputArea.showSelection();
				return;
			}

			int position=inputArea.getCaretOffset();
			if (position < lastPosition) {
				lastPosition=inputArea.getText().length();
				inputArea.setCaretOffset(lastPosition);
				event.doit=false;
				return;
			}

			keyTyped(event);
		}

		public void keyReleased(KeyEvent event) {
			int ch=event.keyCode;
			if (ch == SWT.HOME) {
				inputArea.setCaretOffset(lastPosition);
			}
			if (ch == SWT.ARROW_UP || ch == SWT.ARROW_DOWN) {
				event.doit=false;
				return;
			}
			if (ch == SWT.LEFT) {
				int newPosition=inputArea.getCaretOffset();
				if (newPosition <= lastPosition) {
					event.doit=false;
					return;
				}
			}

			if (ch == SWT.BS) {
				int newPosition=inputArea.getCaretOffset();
				if (newPosition <= lastPosition) {
					event.doit=false;
					// workaround since consume on backspace key does not work
					inputArea.setCaretOffset(lastPosition);
					return;
				}
			}
		}

		private void reset() {
			seq++;
			writePrompt();
			lastPosition=inputArea.getText().length();
			inputArea.setCaretOffset(lastPosition);
		}

		public void keyTyped(KeyEvent event) {
			if (event.keyCode == SWT.ESC || event.keyCode == SWT.PAUSE) {
				reset();
				return;
			}
			commandPointer=0;
			int position=inputArea.getCaretOffset();
			if (position < lastPosition) {
				inputArea.setCaretOffset(inputArea.getText().length());
			}
			char ch=event.character;

			int modifier=event.stateMask;
			if (modifier == SWT.CONTROL && (ch == 'c' || ch == 'C')) {
				inputArea.copy();
			}
			if (modifier == SWT.CONTROL && (ch == 'v' || ch == 'V')) {
				inputArea.paste();
			}

			if (ch == '\r') {
				String input=inputArea.getText().substring(lastPosition - 1);
				if (input.trim().length() == 0) {
					inputArea.setText(inputArea.getText().trim() + " ");
					inputArea.setCaretOffset(lastPosition + 1);
					event.doit=false;
					return;
				}
				commandList.push(input);
					executeCode(input);
//				}
				lastPosition=inputArea.getText().length();
				inputArea.setCaretOffset(lastPosition);
				inputArea.showSelection();
				inputArea.setFocus();
			}
		}
	}

	public void executeCode(String in) {
		System.out.println("input=" + in);
		MyRunnable op=null;
		ProgressMonitorDialog dialog=null;
		String mesg=null;
		try {
			op=new MyRunnable(in,project,((IFileEditorInput)getEditorInput()).getFile());
			dialog=new ProgressMonitorDialog(getSite().getShell());
			dialog.run(true, true, op);
			mesg=op.getMesg();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if (mesg != null) {
			inputArea.append(mesg);
		} else {
			inputArea.append(op.getResult());
		}
		writeHistory(in);
		seq++;
		writePrompt();
	}

	private void writeHistory(String in) {
		this.isSaved=false;
		String out=in.replaceAll("\n", " ").replaceAll("[?]", "");
		HistoryElement element=new HistoryElement(seq + "", new Date(), out
				.trim(), in);
		historyListModel.addHistoryElement(element);
	}
//
//	private String find(String s) {
//		return ("ans =" + '\n');
//		// writePrompt();
//	}

	private void writePrompt() {
		inputArea.append("\n");
		inputArea.append(seq + prompt);
		inputArea.setFocus();
	}

	public void setFocus() {
		inputArea.setFocus();
		inputArea.setCaretOffset(lastPosition);
	}

	private List memoryListeners=new ArrayList();
	private List historyListeners=new ArrayList();

	public void addSelectionChangedListener(ISelectionChangedListener listener) {
		if (listener instanceof PHPConsoleVariablesView) {
			if (!memoryListeners.contains(listener))
				memoryListeners.add(listener);
		} else if (listener instanceof HistoryView) {
			if (!historyListeners.contains(listener))
				historyListeners.add(listener);
		} else {
		}
	}

	public ISelection getSelection() {
		return this;
	}

	public void removeSelectionChangedListener(
			ISelectionChangedListener listener) {
		if (listener instanceof PHPConsoleVariablesView) {
			memoryListeners.remove(listener);
		} else if (listener instanceof HistoryView) {
			historyListeners.remove(listener);
		} else {
		}
	}

	public void setSelection(ISelection selection) {
	}

	public boolean isEmpty() {
		return false;
	}

	public HistoryListModel getHistoryListModel() {
		return historyListModel;
	}

}
